#ifndef CAN_CHANNEL_H__
#define CAN_CHANNEL_H__

#include "../../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h"
#include "../../Utils/Inc/ring_buffer.h"
#include "../error/error.h"
#include "can.h"
#include <stdint.h>

#define CAN_MAC_QUEUE_SIZE 16

#define CAN_STD_CMD_QUEUE_SIZE 10
#define CAN_STD_CMD_ELEMENT_SIZE 10

#define CAN_EXT_CMD_QUEUE_SIZE 1024

#define FRAME_DATA 0
#define FRAME_REMOTE 1

#define IDTYPE_STDID 0
#define IDTYPE_EXTID 1

enum CanFrameType {
  CAN_FRAME_STD,
  CAN_FRAME_EXT,

  CAN_FRAME_STD_DATA,
  CAN_FRAME_EXT_DATA,
  CAN_FRAME_STD_REMOTE,
  CAN_FRAME_EXT_REMOTE,

  CAN_FRAME_INVALID
};

enum CanChannelNumber {
  CAN_CH_1,
  CAN_CH_2,

  CAN_CH_MAX
};

typedef struct CanPacket {
  CanChannelNumber ch;
  CanFrameType ft;
  uint32_t id;

  uint16_t length;
  uint8_t *data;
} CanPacket_t;

typedef struct CanStdDataFrame {
  union {
    struct {
      uint16_t msg_id : 9;   // message id
      uint16_t reserved : 1; // reserved, main controller fill 0, module fill 1
      uint16_t type : 1;   // indicates type of this frame, 0 = request, 1 = ack
      uint16_t length : 5; // indicates length of data field: 1 - 8
    } bits;

    uint16_t val;
  } id; // ID field of CAN standard data frame

  uint8_t data[8]; // data filed of CAN standard data frame
} CanStdDataFrame_t;

typedef bool (*CANIrqCallback_t)(CanStdDataFrame_t &cmd);

class CanChannel {
public:
  ErrCode Init(CANIrqCallback_t irq_cb);

  ErrCode Write(CanPacket_t &packet);

  int32_t Read(CanFrameType ft, uint8_t *pdu, int32_t l);

  int32_t Available(CanFrameType ft);

  void Irq(CanChannelNumber ch, uint8_t fifo_index);

  RingBuffer<uint8_t> &ext_cmd() { return ext_cmd_; }

private:
  RingBuffer<uint32_t> mac_id_;
  RingBuffer<uint8_t> ext_cmd_;

  CanStdDataFrame_t std_cmd_[CAN_STD_CMD_QUEUE_SIZE];
  uint8_t std_cmd_r_;
  uint8_t std_cmd_w_;
  uint8_t std_cmd_in_q_;

  CANIrqCallback_t irq_cb_;

  osMutexId_t lock_[CAN_CH_MAX]; // cmsis_os2 互斥量
};

extern CanChannel can;

namespace Canbus {
uint32_t CanSendPacked(uint32_t ID, uint8_t IDType, uint8_t PortNum,
                       uint8_t FrameType, uint8_t DataLen, uint8_t *pData);
uint8_t CanbusParseData(CAN_HandleTypeDef *hcan, uint32_t *ID, uint8_t *IDType,
                        uint8_t *FrameType, uint8_t *pData, uint8_t *Len,
                        uint8_t FIFONum);
} // namespace Canbus

/* 创建互斥信号量，并用其控制中断开关，两个互斥量属性相同 */
const osMutexAttr_t can_mutex_attr = {
    "can_mutex_attr",
    osMutexRecursive | osMutexPrioInherit, // 允许递归和优先级继承
    NULL,                                  // 控制块内存
    0U                                     // 控制块大小
};

#endif